#ifndef _ML_CLUSTREAM_DATA_CLUSTERING_H_
#define _ML_CLUSTREAM_DATA_CLUSTERING_H_
#include "MLCommon.h"
#include "MLDataClustering.h"
#include "MLDataSet.h"
#include "MLDataVector.h"
#include "MLDataPoint.h"

using namespace mylablib;

///////////////////////////////////////////////////////////////////////////
// CMLCluStreamMicroCluster
class CMLCluStream;
class CMLCluStreamMicroCluster
{
public:
	// Constructor and Destructor
	CMLCluStreamMicroCluster(void);
	~CMLCluStreamMicroCluster(void);

public:
	// Methods
	int Create(const CMLDataSet& dataSet,CMLCluStream* pClustering, int nId, int nTimeStamp);
	
	int Merge(const CMLCluStreamMicroCluster* pMicroCluster);

	int Absorb(const CMLDataPoint& p, int nTimeStamp);

	inline int GetId(void) const { return m_nId; };

	inline int GetDataPointTimeStamp(int nIndex) const { return m_vTimeStamps[nIndex]; };

	inline const CMLDataVector<double>& GetLS(void) const { return m_vLS; };
	inline const CMLDataVector<double>& GetSS(void) const { return m_vSS; };
	inline double GetTimeStampMean(void) const { return m_dTimeStampLS / (double)GetDataPointCount(); };
	inline double GetLastTimeStampMean(int nLast) const 
	{
		if (nLast >= (int)m_vTimeStamps.size())
			return GetTimeStampMean();
		else
		{
			double dTimeMean = 0.0;
			int i;
			for (i=(int)m_vTimeStamps.size()-1; i>=(int)m_vTimeStamps.size()-nLast; i--)
			{
				dTimeMean += m_vTimeStamps[i];
			}
			return dTimeMean / (double)nLast;
		}
	}

	inline CMLDataPoint GetCenter(void) const 
	{ 
		return m_vLS / GetDataPointCount(); 
	}

	inline double GetRadius(void) const 
	{
		int N = GetDataPointCount();
		CMLDataPoint tmp = (m_vSS/N) - ((m_vLS/N)^2);
		return sqrt(tmp.Norm1());
	}

	inline int GetDataPointCount() const { return m_nDataPointCount; };
	inline bool IsEmpty() const { return GetDataPointCount() == 0; };

	inline void SetDistFunc(IMLDataPointDistanceFunc* func) { m_pDistFunc = func; };
	inline IMLDataPointDistanceFunc* GetDistFunc(void) const { return m_pDistFunc; };

private:
	// Fields
	int m_nId;
	int m_nDataPointCount;
	std::deque<int> m_vTimeStamps;
	CMLDataVector<double> m_vLS;
	CMLDataVector<double> m_vSS;
	double m_dTimeStampLS;
	double m_dTimeStampSS;
	IMLDataPointDistanceFunc* m_pDistFunc;
	CMLCluStream* m_pClustering;
};

class CMLCluStreamSnapshot
{
public:
	// Constructor and Destructor
	CMLCluStreamSnapshot(void);
	~CMLCluStreamSnapshot(void);
	CMLCluStreamSnapshot(const CMLCluStreamSnapshot& src);

public:
	// Types
	struct CMicroClusterFeature_
	{
		int nId;
		CMLDataVector<double> vLS;
		int nNumDataPoints;
	};

public:
	// Properties
	inline int GetNumCFs(void) const { return (int)m_vCFs.size(); };
	inline CMLDataPoint GetCenter(int nIndex) const { return m_vCFs[nIndex]->vLS / m_vCFs[nIndex]->nNumDataPoints; };
	inline int GetDataPointCount(int nIndex) const { return m_vCFs[nIndex]->nNumDataPoints; };
	inline const CMicroClusterFeature_* GetCF(int nIndex) const { return m_vCFs[nIndex]; };
	inline const std::vector<CMicroClusterFeature_*>& GetCFs(void) const { return m_vCFs; };

	inline const CMicroClusterFeature_* FindCF(int nId) const
	{
		std::map<int, CMicroClusterFeature_*>::const_iterator Iter = m_mapCFs.find(nId);
		if (Iter == m_mapCFs.end())
			return NULL;
		else
			return (*Iter).second; 
	}

public:
	// Methods
	const CMLCluStreamSnapshot& operator=(const CMLCluStreamSnapshot& src);
	int Create(const std::vector<CMLCluStreamMicroCluster*>& vMicroClusters);
	int Clear(void);
	int Subtract(const CMLCluStreamSnapshot& prevSnap);

private:
	// Fields
	std::map<int, CMicroClusterFeature_*> m_mapCFs;
	std::vector<CMicroClusterFeature_*> m_vCFs;

};


///////////////////////////////////////////////////////////////////////////
// CMLCluStream
class CMLCluStream : public CMLIncrementalDataClustering
{
public:
	// Constructor and Destructor
	CMLCluStream(void);
	~CMLCluStream(void);

public:
	// Interfaces
	int SetParameters(const std::wstring& wstrParams);
	int Insert(const CMLDataPoint& dataPoint);
	int GetClusters(const std::wstring& params, std::vector<CMLDataCluster>& vClusters);

public:
	// Extra Methods
	int CreateMacroClusters(int nK, std::vector<CMLDataCluster>& vClusters) const;
	int CreateMacroClusters(int nK, std::vector<CMLDataCluster>& vClusters, const CMLCluStreamSnapshot& prevSnap) const;
	int SaveSnapshot(CMLCluStreamSnapshot& snap) const;
	inline const std::vector<CMLCluStreamMicroCluster*>& GetMicroClusters(void) const
	{
		return m_vpMicroClusters;
	}
	

public:
	// Properties
	inline int  GetNumMicroClusters(void)     { return (int)m_vpMicroClusters.size(); };
	inline void SetInitNumber(int nNum)       { m_nDataPointInitNumber = nNum; };
	inline int  GetInitNumber(void) const     { return m_nDataPointInitNumber; };
	inline void SetInitNumMicroClusters(int nNum) { m_nInitMicroClusters = nNum; };
	inline int  GetInitNumMicroClusters(void) const { return m_nInitMicroClusters; };
	inline int  GetTotalPointNumber(void) const { return m_nDataPointTotalNumber; };
	inline void SetMaxMicroClusterRadius(double dMax) { m_dMaxMicroClusterRadius = dMax; };
	inline void SetMaxTimeStampBelow(double dMax) { m_dMaxTimeStampBelow = dMax; };
	inline void SetMaxNumMicroClusters(int nMax) { m_nMaxNumMicroClusters = nMax; };
	inline void SetNumLastTimeCheck(int nLast) { m_nNumLastTimeCheck = nLast; };
	inline int  GetNumLastTimeCheck(void) const { return m_nNumLastTimeCheck; };

private:
	// Impelementations
	int InitMicroClusters(void);
	int ReleaseMicroClusters(void);
	int InsertToMicroCluster(const CMLDataPoint& dataPoint);
	CMLCluStreamMicroCluster* FindNearestMicroCluster(const CMLDataPoint& point, double& dDist);
	int MergeClosestTwoMicroClusters(void);
	int CheckOutlierMicroCluster(void);
	static int MacroClustering(int nK, std::vector<CMLDataCluster>& vMacroClusters, const CMLCluStreamSnapshot& snap);
	
private:
	// Fields
	CMLDataSet m_DataSet;
	std::vector<CMLCluStreamMicroCluster*> m_vpMicroClusters;
	int m_nDataPointInitNumber;
	int m_nDataPointTotalNumber;
	int m_nInitMicroClusters;
	double m_dMaxMicroClusterRadius;
	double m_dMaxTimeStampBelow;
	int m_nMaxNumMicroClusters;
	int m_nTimeClock;
	int m_nNumLastTimeCheck;
	int m_nMicroClusterIdCounter;
};

#endif
